var Model, express = require("express"),
  bodyParser = require("body-parser"),
  fs = require("fs"),
  readline = require("readline"),
  multer = require("multer"),
  app = express(),
  https = require("https"),
  http = require("http"),
  models = {},
  $ = require("./tool");
require("body-parser-xml")(bodyParser), app.use(bodyParser.json({
  limit: "100mb"
})), app.use(bodyParser.urlencoded({
  limit: "100mb",
  extended: !0
})), app.use(function (e, s, n) {
  s.setTimeout(6e5, function () {
    return s.status(408).send({
      errorMsg: "请求超时"
    })
  }), n()
}), app.use(function (e, s, n) {
  s.sendEncode = function (e) {
    if (/dev/i.test(process.env.NODE_ENV)) s.send(e);
    else {
      let n = new $.Base64;
      s.send(n.encodeURI("string" == typeof e ? e : JSON.stringify(e)))
    }
  }, n()
});
var nm = {
  $: $,
  app: app,
  redis: {},
  db: require("./SQLHelper"),
  init: function (e) {
    if (e.redis) {
      let s = e.redis;
      const n = require("redis");
      s.dbs = s.dbs || {}, s.dbs.session = s.dbs.session || 0;
      for (let e in s.dbs) nm.redis[e] = {
        client: null,
        clientTime: 0,
        init() {
          let r = (new Date).getTime();
          return (!this.client || this.client && r - this.clientTime > 6e4) && (this.client = n.createClient({
            ...s,
            db: s.dbs[e]
          }), this.clientTime = r), this.client
        },
        get() {
          let e = this.init();
          e.get.apply(e, arguments)
        },
        set() {
          let e = this.init();
          e.set.apply(e, arguments)
        },
        expire() {
          let e = this.init();
          e.expire.apply(e, arguments)
        }
      }
    }
    e.api = $.merge({
      app_group_menu: {
        sort: {
          level: 1,
          sort: -1
        },
        projection: {
          menuId: "id"
        },
        join: {
          menuId: {
            type: "INNER",
            name: "menu",
            key: "id",
            query: {
              status: {
                $ne: 0
              }
            },
            projection: {
              name: 1,
              sort: 1,
              url: 1,
              parentId: 1
            }
          }
        }
      },
      classifySort: {
        sort: {
          level: 1,
          sort: -1
        }
      },
      groupMenu_group_menu: {
        sort: {
          level: 1,
          sort: -1
        },
        projection: {
          menuId: 1
        },
        join: {
          menuId: {
            type: "INNER",
            name: "menu",
            key: "id",
            query: {
              status: {
                $ne: 0
              }
            },
            projection: {
              name: 1,
              sort: 1,
              parentId: 1
            }
          }
        }
      },
      unitUser_unit: {
        projection: {
          unitId: "value",
          name: 1
        }
      },
      unitUser_user: {
        projection: {
          unitId: 1,
          id: 1,
          userName: 1,
          nickName: 1,
          name: 1
        }
      },
      userGroup_group_user: {
        projection: {
          groupId: 1
        },
        join: {
          groupId: {
            type: "INNER",
            name: "group",
            query: {
              status: {
                $ne: 0
              }
            },
            projection: {
              name: 1
            }
          }
        }
      },
      userList_user: {
        projection: {
          photo: 1,
          userName: 1,
          nickName: 1,
          name: 1,
          sex: 1,
          phone: 1,
          IDCard: 1,
          brisday: 1,
          address: 1,
          status: 1,
          id: 1
        }
      }
    }, e.api || {}), global.Config = e, Model = require("./Model"), this.setModel(e.models);
    var s = this,
      n = Config.router;
    app.use(bodyParser.xml({
      limit: "1MB",
      xmlParseOptions: {
        normalize: !0,
        normalizeTags: !0,
        explicitArray: !1
      }
    })), Config.AccessControlAllow = Config.AccessControlAllow || {}, app.all("*", function (e, s, n) {
      s.header("Access-Control-Allow-Origin", Config.AccessControlAllow.Origin || "*"), s.header("Access-Control-Allow-Methods", Config.AccessControlAllow.Methods || "PUT,POST,GET,DELETE,OPTIONS"), s.header("Access-Control-Allow-Headers", "X-Requested-With,Content-Type,token,unitId,unitid,userId,userid" + (Config.AccessControlAllow.Headers ? "," + Config.AccessControlAllow.Headers : "")), n()
    });
    var r = function (e, n, r) {
        try {
          models[e.params.name] ? (e.query.sql ? ("{" != e.query.sql.charAt(0) ? e.sql = Config.api[e.query.sql] : e.sql = JSON.parse(e.query.sql || "{}"), e.query.sql = null) : e.body.sql && ("string" == typeof e.sql ? e.sql = Config.api[e.body.sql] : "object" == e.sql && (e.sql = e.body.sql), e.body.sql = null), function e(s, n) {
            if (!n) return !1;
            var r;
            if (n.projection) {
              for (var o in r = {}, n.projection) Config.models[s][o].unModel || (r[o] = n.projection[o]);
              n.projection = r
            }
            if (n.join)
              for (var o in n.join) e(n.join[o].name, n.join[o])
          }(e.sql), s.loginRouter(e, n, r)) : n.send({
            errorMsg: "无效请求!!!"
          })
        } catch (s) {
          n.send({
            errorMsg: "请求数据错误"
          }), console.log("请求数据错误", {
            headers: e.headers,
            err: s,
            body: e.body,
            query: e.query,
            originalUrl: e.originalUrl,
            ip: e.ip
          })
        }
      },
      o = [];

    function i(e) {
      return __dirname.replace(/source$/, "") + "uploads/" + e
    }
    for (var t in app.get("/areacode", function (e, s) {
        if (o.length) s.send(o);
        else {
          var n = readline.createInterface({
            input: fs.createReadStream("./source/areaCode.txt"),
            crlfDelay: 1 / 0
          });
          n.on("line", e => {
            var s = e.charAt(0) + e.charAt(1),
              n = e.charAt(2) + e.charAt(3),
              r = e.charAt(4) + e.charAt(5),
              i = s + n + r,
              t = "",
              a = 0,
              d = e.replace(/\d|\s/g, "");
            if (!d || !/^\d+$/.test(i)) return !1;
            switch ("00" == n ? t = "" : "00" == r ? (t = s + "0000", a = 1) : t = s + n + "00", /710000|810000|820000/.test(i) || o.push({
                name: d,
                id: i,
                parentId: t,
                capital: a
              }), i) {
              case "110000":
                o.push({
                  name: "北京市",
                  id: "110100",
                  parentId: "110000",
                  capital: 1
                });
                break;
              case "120000":
                o.push({
                  name: "天津市",
                  id: "120100",
                  parentId: "120000",
                  capital: 1
                });
                break;
              case "310000":
                o.push({
                  name: "上海市",
                  id: "310100",
                  parentId: "310000",
                  capital: 1
                });
                break;
              case "500000":
                o.push({
                  name: "重庆市",
                  id: "500100",
                  parentId: "500000",
                  capital: 1
                }), o.push({
                  name: "县",
                  id: "500200",
                  parentId: "500000",
                  capital: 0
                })
            }
          }), n.on("close", function () {
            s.send(o)
          })
        }
      }), app.post("/login", function (e, n) {
        e.body.userName ? models.user.find({
          userName: e.body.userName,
          status: 1
        }, async function (r) {
          r.id || (r = await models.user.findSync({
            phone: e.body.userName,
            status: 1
          })), r.errorMsg ? n.send(r) : r.passWord != e.body.passWord ? n.send({
            errorMsg: "输入密码错误!"
          }) : 1 == r.miniAppUse || "WeChatMiniApp" != e.body.platform && "AlipayMiniApp" != e.body.platform ? s.setSessionRouter(e, n, {
            unitId: r.unitId,
            userId: r.id,
            userName: r.userName
          }, s => {
            var o = $.merge(r, {
              token: s.id,
              id: r.id,
              passWord: "",
              IDCard: ""
            });
            models.group_user.list({
              userId: r.id
            }, function (s) {
              s.errorMsg ? n.send(s) : !r.miniAppUse || "WeChatMiniApp" != e.body.platform && "AlipayMiniApp" != e.body.platform ? s.length > 0 ? (o.groups = s, n.sendEncode(o)) : n.send({
                errorMsg: "无法登录管理平台,请联系管理员分配权限!"
              }) : (o.groups = s, n.sendEncode(o))
            }, {
              projection: {
                groupId: !0
              },
              join: {
                groupId: {
                  type: "INNER",
                  name: "group",
                  key: "id",
                  query: {
                    status: 1
                  },
                  projection: models.group.schemas
                }
              }
            })
          }) : n.send({
            errorMsg: "请联系管理员授权小程序登录权限!"
          })
        }) : n.send({
          errorMsg: "用户名不能为空!"
        })
      }), app.post("/changePassWord", function (e, s) {
        e.body.userName ? models.user.find({
          userName: e.body.userName
        }, function (n) {
          n.errorMsg ? s.send(n) : n.id ? n.passWord != e.body.passWord ? s.send({
            errorMsg: "旧密码输入错误!"
          }) : n.passWord == e.body.newPassWord ? s.send({
            errorMsg: "新密码不能与旧密码一样!"
          }) : models.user.update({
            id: n.id
          }, {
            passWord: e.body.newPassWord
          }, function (e) {
            s.sendEncode(e)
          }) : s.send({
            errorMsg: "用户不存在!"
          })
        }) : s.send({
          errorMsg: "抱歉，您不能这样做！"
        })
      }), app.get("/logs", function (e, s) {
        for (var n = fs.readdirSync("./logs"), r = [], o = 0; o < n.length; o++) r.push('<div><a target="_blank" href="/logs/' + n[o] + '">' + n[o] + "</a></div>");
        s.send(r.join(""))
      }), app.delete("/sessions", this.loginRouter, function (e, s) {
        models.session.remove({
          createTime: {
            $lte: (new Date).getTime() - 24 * (e.query.day || 3) * 60 * 60 * 1e3
          }
        }, function (e) {
          s.sendEncode(e)
        }, {
          real: !0
        })
      }), app.get("/logout", function (e, s) {
        e.headers.token ? models.session.update({
          id: e.headers.token
        }, {
          outTime: $.date(new Date).Format("yyyy-MM-dd hh:mm:ss")
        }, function (e) {
          s.sendEncode(e)
        }) : s.send({})
      }), app.post("/upload", this.loginRouter, multer({
        preservePath: !0,
        storage: multer.diskStorage({
          destination: "./uploads",
          filename: function (e, n, r) {
            var o = n.originalname.split(".");
            r(null, s.$.guid() + "." + o[o.length - 1])
          }
        })
      }).single("file"), function (e, s) {
        e.query.authorization && (e.file.authorization = 1), e.file.unitId = e.unitId, models.file.insert(e.file, function (e) {
          s.send(e)
        })
      }), app.get("/file/:id", function (e, n) {
        models.file.find({
          id: e.params.id
        }, function (r) {
          if (r.errorMsg) return n.send(r), !1;
          1 == r.authorization ? s.loginRouter({
            headers: {
              token: e.query.token,
              unitId: e.query.unitId
            }
          }, n, function () {
            e.query.download ? n.download(i(r.filename), r.originalname) : n.sendFile(i(r.filename))
          }) : e.query.download ? n.download(i(r.filename), r.originalname) : n.sendFile(i(r.filename))
        })
      }), app.route("/nickNames").get(function (e, s) {
        let n = {};
        for (let e in models) models[e].nickName && (n[e] = models[e].nickName);
        s.send($.merge(n, Config.nickNames || {}))
      }), app.route("/model/:name").get(function (e, s) {
        var n = {};
        for (var r in models[e.params.name].schemas) {
          var o = models[e.params.name].schemas[r];
          /TABLE_/.test(r) || ("string" == typeof o && (o = {
            label: o
          }), o.label && !o.unModel && (o.id = r, n[r] = o, delete n[r].data_type, delete n[r].varType))
        }
        s.sendEncode(n)
      }), app.route("/api/model/:name").get(r, function (e, s) {
        if (e.query._ = null, e.query.join) {
          e.sql = e.sql || {}, e.sql.join = e.sql.join || {};
          for (var n = e.query.join.split(","), r = 0; r < n.length; r++) e.sql.join[n[r] + "Id"] = {
            type: "LEFT",
            name: n[r],
            projection: {
              name: n[r] + "Name"
            }
          };
          e.query.join = null
        }
        e.query.id && /^\w+$/.test(e.query.id) && !e.query.pageNum && !e.query.pageSize ? models[e.params.name].find(e.query, function (e) {
          s.sendEncode(e)
        }, e.sql) : (Config.models[e.params.name].sort && (e.sql = e.sql || {}, e.sql.sort || (e.sql.sort = {}, models[e.params.name].schemas.sort && (e.sql.sort.sort = -1), models[e.params.name].schemas.createTime && (e.sql.sort.createTime = 1))), models[e.params.name].schemas.unitId && (e.unitId && (e.query.unitId = e.query.unitId || {
          $regex: e.unitId
        }), e.query.unitId = "all" == e.query.unitId ? void 0 : e.query.unitId), models[e.params.name].list(e.query, function (e) {
          s.sendEncode(e)
        }, e.sql))
      }).post(r, function (e, s) {
        e.unitId && (e.body.unitId = e.body.unitId || e.unitId), e.query.unitId = "all" == e.query.unitId ? void 0 : e.query.unitId, models[e.params.name].insert(e.body, function (n) {
          if (!n.errorMsg)
            for (var r in Config.models[e.params.name]) Config.models[e.params.name][r].unModel && n[r] && (n[r] = void 0);
          s.sendEncode(n)
        }, e.sql)
      }).put(r, function (e, s) {
        var n = e.body.query || {};
        e.body.id && (n.id = e.body.id), models[e.params.name].update(n, e.body, function (e) {
          s.sendEncode(e)
        }, e.sql)
      }).delete(r, function (e, s) {
        e.unitId && (e.query.unitId = e.query.unitId || {
          $regex: e.unitId
        }), e.query.unitId = "all" == e.query.unitId ? void 0 : e.query.unitId, models[e.params.name].remove(e.query, function (e) {
          s.sendEncode(e)
        }, e.sql)
      }), n) "default" == t ? app.use("/", express.static(n[t])) : (n[t].html && app.use("/" + t, express.static(n[t].html)), n[t].server && app.use("/" + t, require(n[t].server)));
    app.use(function (e, s, n, r) {
      0 == s.originalUrl.indexOf("/file/") ? (console.log("500 err 加载图片出错," + s.originalUrl), n.status(500).send({
        errorMsg: "未知错误"
      })) : (console.log("500 err", {
        headers: s.headers,
        err: e,
        body: s.body,
        query: s.query,
        originalUrl: s.originalUrl,
        ip: s.ip
      }), n.status(500).send({
        errorMsg: "未知错误"
      }))
    })
  },
  initDB: function () {
    "-init" == process.argv[2] && require("../init")(models, process.argv[3])
  },
  models: function () {
    return models
  },
  log: function (e, s) {
    s ? models.log.insert({
      content: e,
      type: s,
      desoptions: $.date().Format("yyyy-MM-dd hh:mm:ss")
    }) : console.log(e)
  },
  setModel: function (e) {
    for (var s in e = e || {}, e = this.$.merge(require("./models"), e)) {
      var n = "id";
      for (var r in e[s]) /TABLE_/.test(r) || "string" != typeof e[s][r] || (e[s][r] = {
        label: e[s][r]
      }), (e[s][r].PRIMARYKEY || e[s][r].data_type && /PRIMARY KEY/.test(e[s][r].data_type)) && (n = r);
      Config.disDefaultModels[s] || (e[s].id = this.$.merge({
        label: "ID",
        unList: !0
      }, e[s].id || {}), e[s].createTime = this.$.merge({
        label: "创建时间",
        type: "datetime",
        unModel: !0
      }, e[s].createTime || {}), e[s].createTimeString = this.$.merge({
        label: "创建时间",
        unModel: !0,
        desc: "创建时间字符串"
      }, e[s].createTimeString || {}), e[s].updateTime = this.$.merge({
        label: "更新时间",
        type: "datetime",
        unModel: !0
      }, e[s].updateTime || {}), e[s].updateTimeString = this.$.merge({
        label: "更新时间",
        unModel: !0,
        desc: "更新时间字符串"
      }, e[s].updateTimeString || {}), e[s].state = this.$.merge({
        label: "",
        data_type: "INT",
        defaultValue: 1,
        unModel: !0,
        desc: "逻辑删除控制:1(默认)、0(已逻辑删除)"
      }, e[s].state || {}), e[s].unitId = this.$.merge({
        label: "",
        index: !0,
        desc: "数据隔离字段"
      }, e[s].unitId || {})), models[s] = new Model(s, e[s], n)
    }
    Config.models = e
  },
  router: function (e) {
    e.call(app, app)
  },
  loginRouter: function (e, s, n) {
    var r, o = e.headers.token,
      i = e.headers.unitId || e.headers.unitid;

    function t(t) {
      !t.errorMsg && t.id && !t.outTime && new Date - t.createTime <= (1e3 * Config.sessionTime || 72e5) ? (e.unitId = i || t.unitId, e.session = t, r || $.Storage.set("session_" + o, t), n()) : ($.Storage.remove("session_" + o), s.status(400).send({
        code: 400,
        errorMsg: "token失效"
      }))
    }
    "OPTIONS" == e.method ? n() : o ? (r = $.Storage.get("session_" + o)) ? t(r) : nm.redis && nm.redis.session ? nm.redis.session.get("session_" + o, (e, n) => {
      try {
        t(JSON.parse(n))
      } catch (e) {
        s.status(400).send({
          code: 400,
          errorMsg: "token失效"
        })
      }
    }) : models.session.find({
      id: o
    }, function (e) {
      t(e)
    }) : s.status(400).send({
      code: 400,
      errorMsg: "无token"
    })
  },
  setSessionRouter: function (e, s, n, r) {
    var o = new Date;
    n.id = n.id || $.snowflake(), n.platform = e.body.platform, n.type = e.body.type || 1, n.address = e.ip, n.inTime = $.date(o).Format("yyyy-MM-dd hh:mm:ss"), n.createTime = o.getTime(), n.unitId = n.unitId || Config.unitId, nm.redis.session ? (nm.redis.session.set("session_" + n.id, JSON.stringify(n)), nm.redis.session.expire("session_" + n.id, Config.sessionTime || 7200), r(n)) : models.session.insert(n, r)
  },
  updateVersionNumber: function (e, s) {
    for (var n = 0; n < e.length; n++) fs.writeFileSync(e[n], fs.readFileSync(e[n], "utf-8").replace(/__\d+__/gim, "__" + s + "__"))
  },
  server: function () {
    var e = http.createServer(app);
    if (443 == Config.port || 44380 == Config.port) {
      var s = {};
      Config.pem_key && Config.pem_cer ? s = {
        key: fs.readFileSync(Config.pem_key, "utf8"),
        cert: fs.readFileSync(Config.pem_cer, "utf8")
      } : Config.pfx && Config.passphrase && (s = {
        pfx: fs.readFileSync(Config.pfx),
        passphrase: Config.passphrase
      });
      var n = https.createServer(s, app);
      n.listen(443, "0.0.0.0", function () {
        Config.ip = n.address().address, console.log("HTTPS Server is running on: http://%s:%s", Config.ip, n.address().port)
      }), 44380 == Config.port && e.listen(80, "0.0.0.0", function () {
        Config.ip = e.address().address, console.log("HTTP Server is running on: http://%s:%s", Config.ip, e.address().port)
      })
    } else e.listen(Config.port, "0.0.0.0", function () {
      Config.ip = e.address().address, console.log("HTTP Server is running on: http://%s:%s", "localhost", e.address().port)
    })
  }
};
process.on("uncaughtException", function (e) {
  e.originalUrl && 0 == e.originalUrl.indexOf("/file/") ? console.log("加载图片出错," + e.originalUrl) : console.log(e)
}), module.exports = nm;